#include "Demo.h"
#include <windowsx.h>

//////////////////////////////////////////////////////////////////////////
// CDemoApp
//////////////////////////////////////////////////////////////////////////
CDemoApp::CDemoApp()
{

}

CDemoApp::~CDemoApp()
{

}

bool CDemoApp::InitInstance()
{
	m_pMainWnd = new CDemoWnd();
	m_pMainWnd->ShowWindow(SW_SHOW);
																   
	return true;
}

//////////////////////////////////////////////////////////////////////////
// CDemoWnd
//////////////////////////////////////////////////////////////////////////
#define MSGHANDLE(nMsg, MsgProc)  {(nMsg), (&CDemoWnd::MsgProc)}

const CDemoWnd::TMSGMAP CDemoWnd::MsgMapTable[] = {
	MSGHANDLE(WM_CREATE,	OnCreate),
	MSGHANDLE(WM_DESTROY,	OnDestroy), 
	MSGHANDLE(WM_PAINT,		OnPaint),
};

CDemoWnd::CDemoWnd()
{
	Create(_T("Drawing Basic"), WS_OVERLAPPEDWINDOW, 0, 0, 800, 480, NULL);
}

CDemoWnd::~CDemoWnd()
{

}

LRESULT	 CDemoWnd::WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	for (int ii = 0; ii < sizeof(MsgMapTable) / sizeof(*MsgMapTable); ii++)
	{
		if (nMsg == MsgMapTable[ii].m_nMsg)
		{
			(this->*MsgMapTable[ii].m_MsgProc)(hWnd, nMsg, wParam, lParam);
			break;
		}
	}

	return __super::FinalWndProc(hWnd, nMsg, wParam, lParam);
}

LRESULT CDemoWnd::OnCreate(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	CenterWindow();

	return 0;
}

LRESULT CDemoWnd::OnDestroy(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	PostQuitMessage(0);

	return 0;
}

LRESULT CDemoWnd::OnPaint(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hDC = BeginPaint(hWnd, &ps);
	
	//--: Set drawing area
	RECT rtClient;
	CopyRect(&rtClient, &ps.rcPaint);
	InflateRect(&rtClient, -20, -20);
	
	//--: calculate every grid's width & height	
	const int nRowCount		= 3;
	const int nColCount		= 2;
	const int nGridWidth	= ((rtClient.right - rtClient.left) / nColCount);
	const int nGridHeight	= ((rtClient.bottom - rtClient.top) / nRowCount);

	RECT* rtGrids			= new RECT[nRowCount * nColCount];

	//--: Draw Grid (3 x 2)
	int nIndex				= 0;
	for (int ii = 0; ii < nRowCount; ii++)
	{
		for (int jj = 0; jj < nColCount; jj++)
		{
			int top		= rtClient.top + ii * nGridHeight;
			int left	= rtClient.left + jj * nGridWidth;
			int right	= left + nGridWidth;
			int bottom	= top + nGridHeight;

			SetRect(&rtGrids[nIndex], left, top, right, bottom);
			DrawRectangle(hDC, rtGrids[nIndex]);
			nIndex++;
		}
	}

	//--: demo drawing for every grid
	struct TDemoData {
		typedef void (CDemoWnd::*DEMOMETHOD)(HDC hDC, const RECT& rtGrid);
		const TCHAR*	m_szDemoInfo;
		DEMOMETHOD		m_fnDemoMethod;
	};	
	TDemoData DemoDataList[] = {
		{_T("Device content infomation"),		&CDemoWnd::DemoDCInfo},
		{_T("A kinds of DC function"),			&CDemoWnd::DemoDCDrawing},
		{_T("Draw text"),						&CDemoWnd::DemoDrawingText},
		{_T("Draw lines"),						&CDemoWnd::DemoDrawingLine},
		{_T("Draw a rectangle"),				&CDemoWnd::DemoDrawingRectangle},
		{_T("Draw a fill rectangle"),			&CDemoWnd::DemoDrawingFillRectangle}
	};
	int nDemoCount	= sizeof(DemoDataList) / sizeof(*DemoDataList);
	assert(nDemoCount <= (nRowCount * nColCount));
	for (int ii = 0; ii < nDemoCount; ii++)
	{
		DrawMarkInfo(hDC, rtGrids[ii], ii + 1, DemoDataList[ii].m_szDemoInfo);
		InflateRect(&rtGrids[ii], -10, -20);
		OffsetRect(&rtGrids[ii], 5, 10);
		(this->*DemoDataList[ii].m_fnDemoMethod)(hDC, rtGrids[ii]);
	}

	delete[] rtGrids;

	EndPaint(hWnd, &ps);

	return 1;
}

LRESULT CDemoWnd::OnLButtonDown(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
		
	return 1;
}

void CDemoWnd::DrawRectangle(HDC hDC, const RECT& rtGrid, COLORREF clrLine)
{
	HPEN hPen = CreatePen(PS_SOLID, 1, clrLine);
	HPEN oldPen = (HPEN)SelectObject(hDC,hPen);
	MoveToEx(hDC, rtGrid.left, rtGrid.top, NULL);
	LineTo(hDC, rtGrid.right, rtGrid.top);
	LineTo(hDC, rtGrid.right, rtGrid.bottom);
	LineTo(hDC, rtGrid.left, rtGrid.bottom);
	LineTo(hDC, rtGrid.left, rtGrid.top);
	SelectObject(hDC, oldPen);
	DeleteObject(hPen);
	return;
}

void CDemoWnd::DrawMarkInfo(HDC hDC, const RECT& rtGrids, int nIndex, const TCHAR* szMarkInfo, COLORREF clrText)
{
	TCHAR szPrompt[128];
	_stprintf_s(szPrompt, 128, _T("%d. %s"), nIndex, szMarkInfo);
	SetTextColor(hDC, clrText);
	TextOut(hDC, rtGrids.left + 1, rtGrids.top + 1, szPrompt, _tcslen(szPrompt));

	return;
}

void CDemoWnd::DemoDCInfo(HDC hDC, const RECT& rtGrid)
{
	int nScrnW		= GetDeviceCaps(hDC, HORZRES);
	int nScrnH		= GetDeviceCaps(hDC, VERTRES);
	int nPixX		= GetDeviceCaps(hDC, LOGPIXELSX);
	int nPixY		= GetDeviceCaps(hDC, LOGPIXELSY);
	int nBitsPixel	= GetDeviceCaps(hDC, BITSPIXEL);
	
	HFONT  hFont	= static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
	HBRUSH hBrush	= static_cast<HBRUSH>(GetCurrentObject(hDC, OBJ_BRUSH));
	HPEN   hPen		= static_cast<HPEN>(GetCurrentObject(hDC, OBJ_PEN));

	LOGPEN		logPen;
	LOGBRUSH	logBrush;
	LOGFONT		logFont;
	GetObject(hPen,		sizeof(LOGPEN),		&logPen);
	GetObject(hBrush,	sizeof(LOGBRUSH),	&logBrush);
	GetObject(hFont,	sizeof(LOGFONT),	&logFont);
	
	TCHAR szDCInfo[512];
	_stprintf_s(szDCInfo, 512, 
				_T("Scrn Info:      %d X %d X %d (W*H*C)\n")
				_T("Pixels Size:  %d X %d\n")
				_T("Pen Info:        Color[RGB(%d:%d:%d)], W*H[%dx%d], Style[%s]\n")
				_T("Brush Info:    Color[RGB(%d:%d:%d)], Style[%s]\n")
				_T("Font Info:       FaceName[%s], Size[%d]\n"),
				nScrnW, nScrnH, nBitsPixel, nPixX, nPixY,
				GetRValue(logPen.lopnColor), GetGValue(logPen.lopnColor), GetBValue(logPen.lopnColor), 
				logPen.lopnWidth.x, logPen.lopnWidth.y, _GetLineStyle(logPen.lopnStyle),
				GetRValue(logBrush.lbColor), GetGValue(logBrush.lbColor), GetBValue(logBrush.lbColor), 
				_GetBrushStyle(logBrush.lbStyle),
				logFont.lfFaceName, logFont.lfHeight
				);
	COLORREF OldClr = SetTextColor(hDC, RGB(10, 0, 0));
	DrawText(hDC, szDCInfo, -1, (LPRECT)&rtGrid, DT_LEFT|DT_VCENTER);
	SetTextColor(hDC, OldClr);
	return;
}

void CDemoWnd::DemoDCDrawing(HDC hDC, const RECT& rtGrid)
{
	//--: Drawing in Desktop's DC 
	{
		RECT rtDesktop;
		GetWindowRect(GetDesktopWindow(), &rtDesktop);
		InflateRect(&rtDesktop, -1, -1);

		HDC hDisDC = CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL);
		
		DrawMarkInfo(hDisDC, rtDesktop, 0, _T("I come from Drawing Basic -- Display DC"), RGB(0, 0, 255));
		DrawRectangle(hDisDC, rtDesktop, RGB(255, 255, 0));
		
		DeleteDC(hDisDC);
	}

	//-- Drawing in Window's DC
	{
		RECT rtWindow;
		GetWindowRect(m_hWnd, &rtWindow);
	
		HDC hWinDC = GetWindowDC(m_hWnd); //GetDCEx(hWnd, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN);  

		_ScreenToClient(m_hWnd, rtWindow);
		InflateRect(&rtWindow, -1, -1);
		DrawMarkInfo(hWinDC, rtWindow, 1, _T("I come from Drawing Basic -- Window DC"), RGB(0, 0, 255));
		DrawRectangle(hWinDC, rtWindow, RGB(255, 0, 0));

		ReleaseDC(m_hWnd, hWinDC);
	}

	//-- Drawing in Client's DC
	{
		RECT rtWindow;
		GetClientRect(m_hWnd, &rtWindow);

		InflateRect(&rtWindow, -1, -1);
		//HRGN hRgnWindow = CreateRectRgn(rtWindow.left, rtWindow.top, rtWindow.right, rtWindow.bottom);
		HDC hWinDC = GetDC(m_hWnd);//GetDCEx(m_hWnd, hRgnWindow, DCX_WINDOW);  

		DrawMarkInfo(hWinDC, rtWindow, 2, _T("I come from Drawing Basic -- Client DC"), RGB(0, 0, 255));
		DrawRectangle(hWinDC, rtWindow, RGB(255, 0, 255));

		ReleaseDC(m_hWnd, hWinDC);
	}

	return;
}

void CDemoWnd::DemoDrawingLine(HDC hDC, const RECT& rtGrid)
{
	HPEN hPen = CreatePen(PS_SOLID, 2, RGB(128, 255, 0));
	HPEN hOldPen = (HPEN)SelectObject(hDC, hPen);

	int nHeight = rtGrid.bottom - rtGrid.top;
	int nWidth = rtGrid.right - rtGrid.left;
	int nX = rtGrid.left + (nWidth >> 1);
	int nY = rtGrid.top + (nHeight >> 1);

	MoveToEx(hDC, nX, rtGrid.top, NULL);
	LineTo(hDC, nX, rtGrid.bottom);

	MoveToEx(hDC, rtGrid.left, nY, NULL);
	LineTo(hDC, rtGrid.right, nY);

	SelectObject(hDC, hOldPen);
	DeleteObject(hPen);

	return;
}

void CDemoWnd::DemoDrawingText(HDC hDC, const RECT& rtGrid)
{
	COLORREF clrOld = SetTextColor(hDC, RGB(255, 128, 0));
	COLORREF clrBk = SetBkColor(hDC, RGB(128, 128, 128));

	const TCHAR* szText1 = _T("using Textout drawing text");
	TextOut(hDC, rtGrid.left, rtGrid.top, szText1, _tcslen(szText1));
	
	const TCHAR* szText3 = _T("using ExtTextOut drawing text");
	ExtTextOut(hDC, rtGrid.left, rtGrid.top + 20, 0, NULL, szText3, _tcslen(szText3), NULL);
	
	int nBKMode = SetBkMode(hDC, TRANSPARENT);
	const TCHAR* szText2 = _T("using Textout drawing text");
	DrawText(hDC, szText2, -1, (LPRECT)&rtGrid, DT_SINGLELINE|DT_CENTER|DT_BOTTOM);

	SetTextColor(hDC, clrOld);
	SetBkColor(hDC, clrBk);
	SetBkMode(hDC, nBKMode);

	return;
}

void CDemoWnd::DemoDrawingRectangle(HDC hDC, const RECT& rtGrid)
{
	LOGBRUSH logBrush;
	logBrush.lbColor	= RGB(0, 0, 255);
	logBrush.lbHatch	= HS_CROSS;
	logBrush.lbStyle	= BS_NULL;
	HBRUSH hBrush		= CreateBrushIndirect(&logBrush);
	HBRUSH hOldBrush	= (HBRUSH)SelectObject(hDC, hBrush);
	Rectangle(hDC, rtGrid.left, rtGrid.top, rtGrid.right, rtGrid.bottom);
	SelectObject(hDC, hOldBrush);
	DeleteObject(hOldBrush);
		
	return;
}

void CDemoWnd::DemoDrawingFillRectangle(HDC hDC, const RECT& rtGrid)
{
	FillRect(hDC, (LPRECT)&rtGrid, (HBRUSH)GetStockObject(DKGRAY_BRUSH));

	return;
}

const RECT& CDemoWnd::_ScreenToClient(HWND hWnd, RECT& rtScreen)
{
	int nWidth	= rtScreen.right - rtScreen.left;
	int nHeight = rtScreen.bottom - rtScreen.top;
	rtScreen.left	= 0;
	rtScreen.top	= 0;
	rtScreen.right	= nWidth;
	rtScreen.bottom	= nHeight;
	
	return rtScreen;
}

const TCHAR* CDemoWnd::_GetLineStyle(UINT nStyle)
{
	static const TCHAR* szLineStyle[] = {
		_T("Solid"),		_T("Dash"),			_T("Dot"),			_T("Dashdot"),	
		_T("Dashdotdot"),  	_T("Invisible"),	_T("Insidefreme")
	};

	const TCHAR* pStyle = _T(" ");
	switch (nStyle)
	{
		case PS_SOLID:			pStyle = szLineStyle[0];	break;
		case PS_DASH:			pStyle = szLineStyle[1];	break;
		case PS_DOT:			pStyle = szLineStyle[2];	break;
		case PS_DASHDOT:		pStyle = szLineStyle[3];	break;
		case PS_DASHDOTDOT:		pStyle = szLineStyle[4];	break;
		case PS_NULL:			pStyle = szLineStyle[5];	break;
		case PS_INSIDEFRAME:    pStyle = szLineStyle[6];	break;
	}
	return pStyle;
}

const TCHAR* CDemoWnd::_GetBrushStyle(UINT nStyle)
{
	static const TCHAR* szBrushStyle[] = {
		_T("DIBPattern"),	_T("DIBPattern8x8"),	_T("DIBPatternPT"),		_T("Hatched"),	
		_T("Hollow"),		_T("Pattern"),			_T("Pattern8x8"),		_T("Solid")
	};

	const TCHAR* pStyle = _T(" ");
	switch (nStyle)
	{
		case BS_DIBPATTERN:			pStyle = szBrushStyle[0];	break;
		case BS_DIBPATTERN8X8:		pStyle = szBrushStyle[1];	break;
		case BS_DIBPATTERNPT:		pStyle = szBrushStyle[2];	break;
		case BS_HATCHED:			pStyle = szBrushStyle[3];	break;
		case BS_HOLLOW:				pStyle = szBrushStyle[4];	break;
		case BS_PATTERN:			pStyle = szBrushStyle[5];	break;
		case BS_PATTERN8X8:			pStyle = szBrushStyle[6];	break;
		case BS_SOLID:				pStyle = szBrushStyle[7];	break;
	}
	return pStyle;
}